package node.exp;

import grammar.Lexer;
import grammar.MyError;
import lexical.Key;
import lexical.Word;
import midcode.MidCode;
import midcode.MidcodeGenerator;
import midcode.OPType;
import node.Node;
import node.func.FuncCall;
import node.func.param.FuncRParams;
import symbol.Function;
import symbol.SymbolTable;

import java.util.ArrayList;

public class UnaryExp implements Node {
    private final Lexer lexer;
    private final SymbolTable table;
    private final Node father;
    private Node unaryExp;
    private FuncCall funcCall;
    private final ArrayList<Word> calTypes = new ArrayList<>();

    public UnaryExp(Lexer lexer, SymbolTable table, Node father) {
        this.lexer = lexer;
        this.table = table;
        this.father = father;
        parseUnaryExp();
    }

    private void parseUnaryExp() {
        if (lexer.isWordType(0, Key.IDENFR) && lexer.isWordType(1, Key.LPARENT)) {
            funcCall = new FuncCall(lexer, table, this);
        } else if (lexer.isWordType(0, Key.PLUS) || lexer.isWordType(0, Key.MINU) || lexer.isWordType(0, Key.NOT)) {
            while (lexer.isWordType(0, Key.PLUS) || lexer.isWordType(0, Key.MINU) || lexer.isWordType(0, Key.NOT)) {
                calTypes.add(lexer.getCurWord());
            }
            unaryExp = new UnaryExp(lexer, table, this);
        } else {
            unaryExp = new PrimaryExp(lexer, table, this);
        }
    }

    public int getValue() {
        int value = 0;
        if (unaryExp instanceof PrimaryExp) {
            value = ((PrimaryExp)unaryExp).getValue();
        } else if (unaryExp instanceof UnaryExp) {
            value = ((UnaryExp)unaryExp).getValue();
            for (Word word : calTypes) {
                if (word.key == Key.MINU) {
                    value = -value;
                }
            }
        }
        return value;
    }

    @Override
    public Node getFather() {
        return father;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        if (unaryExp instanceof UnaryExp) {
            for (Word word : calTypes) {
                sb.append(word.toString()).append("<UnaryOp>\n");
            }
            sb.append(unaryExp.toString());
        } else if (unaryExp instanceof PrimaryExp) {
            sb.append(unaryExp.toString());
        } else {
            assert funcCall != null;
            sb.append(funcCall.toString());
        }
        return sb.append("<UnaryExp>\n").toString();
    }

    @Override
    public String genMidCode() {
        if (funcCall != null) {
            return funcCall.genMidCode();
        } else if (unaryExp != null) {
            int subNum = 0;
            int notNum = 0;
            for (Word word : calTypes) {
                if (word.key == Key.MINU) {
                    subNum++;
                } else if (word.key == Key.NOT) {
                    notNum++;
                }
            }
            MidcodeGenerator mg = MidcodeGenerator.getInstance();
            String tmp = unaryExp.genMidCode();
            if (mg.isNumber(tmp)) {
                int value = Integer.parseInt(tmp);
                value = (subNum % 2 == 0) ? value : -value;
                if (notNum % 2 != 0) {
                    if (value != 0) {
                        value = 0;
                    } else {
                        value = 1;
                    }
                }
                tmp = Integer.toString(value);
            } else {
                if (subNum % 2 != 0) {
                    String dst = mg.genTmp();
                    mg.addMidcode(new MidCode(OPType.SUB, Integer.toString(0), tmp, dst));
                    tmp = dst;
                }
                if (notNum % 2 != 0) {
                    String dst = mg.genTmp();
                    mg.addMidcode(new MidCode(OPType.NOT, tmp, null, dst));
                    tmp = dst;
                }
            }
            return tmp;
        }
        return "";
    }
}

